//////////////////////////////////////////////////////////////////////////
//
// GAM203
// Renderer
// Renderer.h
// 
// Grant Johnson
// Qantm College
// 27/10/2010
//
//////////////////////////////////////////////////////////////////////////

#ifndef RENDERER_H
#define RENDERER_H

#include <list>
#include <d3d9.h>
#include <d3d9types.h>
#include <d3dx9math.h>
#include "Singleton.h"
#include "FreeCamera.h"
#include "Camera.h"
#include "ABCamera.h"

class Octree;
class Drawable;
class Light;
class Text;
class Node;
class Emitter;

struct IDirect3DDevice9;

struct SHADOWVERT
{
	D3DXVECTOR3 Position;
	D3DXVECTOR3 Normal;
};

const D3DVERTEXELEMENT9 Decl[3] =
{
	{ 0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
	{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL,   0 },
	D3DDECL_END()
};

struct CEdgeMapping
{
	int m_anOldEdge[2];  // vertex index of the original edge
	int m_aanNewEdge[2][2]; // vertex indexes of the new edge
	// First subscript = index of the new edge
	// Second subscript = index of the vertex for the edge

public:
	CEdgeMapping()
	{
		FillMemory( m_anOldEdge, sizeof( m_anOldEdge ), -1 );
		FillMemory( m_aanNewEdge, sizeof( m_aanNewEdge ), -1 );
	}
};

class Renderer : public Singleton<Renderer>
{
public:
	Renderer();
	virtual ~Renderer();

	//Type def two list types
	typedef std::list<Drawable*> DrawableObjectList;
	typedef std::list<Emitter*> EmitterList;
	typedef std::list<Light*> LightList;
	typedef std::list<ABCamera*> CameraList;

	//////////////////////////////////////////////////////////////////////////
	// Setup the renderer, debug lines, and FPS text object
	//
	// Paramaters:
	// a_pDevice, A pointer to the direct3d device
	//
	// Returns false if it failed to create the vertex buffer for debug lines
	// Returns true if it setup successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool Setup(IDirect3DDevice9* a_pDevice, float a_width, float a_height);

	//////////////////////////////////////////////////////////////////////////
	// Add an object to the member data object list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to be added
	//
	// Returns false if it failed to add the object
	// Returns true if it added successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddObject(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Remove an object from the member data object list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to object to be removed
	//
	// Returns false if it failed to find the object
	// Returns true if it was removed successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool RemoveObject(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Add an object to the member data object list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to be added
	//
	// Returns false if it failed to add the object
	// Returns true if it added successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddOctreeObject(Drawable* a_pObject);
	bool AddOctreeObjectToRender(Drawable* a_pObject);

	DrawableObjectList* GetOctreeObjectsToRender() { return &m_octreeObjectsToRender; }


	//////////////////////////////////////////////////////////////////////////
	// Remove an object from the member data object list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to object to be removed
	//
	// Returns false if it failed to find the object
	// Returns true if it was removed successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool RemoveOctreeObject(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Add an emitter to the member data object list
	////
	// NOTE: Emitters clean themselves up, so they can be made on the fly
	////
	//
	// Paramaters:
	// a_pObject, A pointer to the object to be added
	//
	// Returns false if it failed to add the object
	// Returns true if it added successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddEmitter(Emitter* a_pEmitter);

	//////////////////////////////////////////////////////////////////////////
	// Remove an emitter from the member data object list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to object to be removed
	//
	// Returns false if it failed to find the object
	// Returns true if it was removed successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool RemoveEmitter(Emitter* a_pEmitter);

	//////////////////////////////////////////////////////////////////////////
	// Add an object to the member data text list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to be added
	//
	// Returns false if it failed to add the object
	// Returns true if it added successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddText(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Remove an object from the member data text list
	//
	// Paramaters:
	// a_pObject, A pointer to the object to object to be removed
	//
	// Returns false if it failed to find the object
	// Returns true if it was removed successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool RemoveText(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Add a stencil to the member data stencil list
	//
	// Paramaters:
	// a_pObject, A pointer to the stencil to be added
	//
	// Returns false if it failed to add the stencil
	// Returns true if it added successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddStencil(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Remove a stencil from the member data stencil list
	//
	// Paramaters:
	// a_pObject, A pointer to the stencil to be removed
	//
	// Returns false if it failed to find the stencil
	// Returns true if it was removed successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool RemoveStencil(Drawable* a_pObject);

	//////////////////////////////////////////////////////////////////////////
	// Adds a light to the member data light list
	//
	// Paramaters:
	// a_pLight, A pointer to the light to be added
	//
	// Returns false if it failed to add the light
	// Returns true if it added successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddLight(Light* a_pLight);

	//////////////////////////////////////////////////////////////////////////
	// Remove a light from the member data light list
	//
	// Paramaters:
	// a_pLight, A pointer to the light to be removed
	//
	// Returns false if it failed to find the light
	// Returns true if it was removed successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool RemoveLight(Light* a_pLight);

	//////////////////////////////////////////////////////////////////////////
	// Render the scene - 
	//
	// Clear the scene, Setup the lights, Pre-render with stencils, render the 
	// world objects, Update and render Fps, Render debug lines.
	//
	// Paramaters:
	// a_dt, current delta time
	// a_pDevice, direct3D device
	//
	// Returns false if it failed to render
	// Returns true if it rendered the scene successfully
	//
	//////////////////////////////////////////////////////////////////////////
	bool Render(float a_dt, IDirect3DDevice9* a_pDevice);

	//////////////////////////////////////////////////////////////////////////
	// Add a debug line to the renderer. 
	//
	// Note: These are removed each render, so must be created each update.
	//
	// Paramaters:
	// a_start, starting position of the line.
	// a_end, ending position of the line.
	// a_colour, default value of white. Colour of the line
	//
	// Returns false if it failed, as the buffer is full or unavailable
	// Returns true if it created the line
	//
	//////////////////////////////////////////////////////////////////////////
	bool AddDebugLine(const D3DVECTOR& a_start, const D3DVECTOR& a_end, D3DCOLOR a_colour = 0xFFFFFFFF);

	//////////////////////////////////////////////////////////////////////////
	// Update the instant and filtered Fps values, then update the text for 
	// both.
	//
	// Paramaters:
	// a_dt, Current Delta Time
	//
	// Returns void
	//
	//////////////////////////////////////////////////////////////////////////
	void UpdateFps(float a_dt);

	//////////////////////////////////////////////////////////////////////////
	// Removes all lights and objects held by the renderer. This is called
	// inside the state manager, whenever a state is changed, in order to clean
	// the renderer.
	//
	// Paramaters:
	// None
	//
	// Returns void
	//
	//////////////////////////////////////////////////////////////////////////
	void RemoveAll();

	bool AddCamera(ABCamera* a_pCamera);
	bool RemoveCamera(ABCamera* a_pCamera);
	ABCamera* GetActiveCamera();

	DrawableObjectList& GetOctreeList() { return m_octreeObjects; }
	LightList& GetLightList() { return m_lights; }
	Octree* GetOctree() { return m_pOctree; }

	void GenerateOctree(int a_max, float a_bottomLeft, float a_topRight);

	int GetNumEmitters() { return m_emitters.size(); }

	void SetDimensions(float a_width, float a_height);

	HRESULT GenerateShadowMesh( IDirect3DDevice9* pd3dDevice, ID3DXMesh* pMesh, ID3DXMesh** ppOutMesh );
	int FindEdgeInMappingTable( int nV1, int nV2, CEdgeMapping* pMapping, int nCount );

	IDirect3DVertexDeclaration9* GetDecl() { return m_pShadowDecl; }

private:

	//List of drawable objects
	DrawableObjectList m_octreeObjects;
	DrawableObjectList m_drawableObjects;
	DrawableObjectList m_textObjects;
	DrawableObjectList m_stencilObjects;

	DrawableObjectList m_octreeObjectsToRender;

	//List of emitters
	EmitterList m_emitters;

	LightList m_lights;

	//List of cameras
	CameraList m_cameras;

	//Debug lines
	struct DebugLineVertexData //vert data for one point in a line
	{
		D3DVECTOR pos;
		D3DCOLOR col;
	};

	static const DWORD DEBUG_LINE_FVF = D3DFVF_XYZ | D3DFVF_DIFFUSE;

	static const int MAX_DEBUG_LINES = 256;
	int m_debugLineCount;
	DebugLineVertexData* m_pDebugLineData;
	IDirect3DVertexBuffer9* m_pDebugLineVertexBuf;

	//Holds instant fps
	float m_instantFps;

	//Holds filtered fps
	float m_filteredFps;

	//String buffer for fps
	char* m_pFpsBuffer;

	//Text object to display fps
	Text* m_pFpsText;

	Octree* m_pOctree;

	//Width and Height of the window
	float m_width;
	float m_height;

	IDirect3DVertexDeclaration9* m_pShadowDecl;
};

#endif //RENDERER_H